<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>part3_06_directory - learning-gem5</title>
        <!-- Custom HTML head -->
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../favicon.svg">
        <link rel="shortcut icon" href="../favicon.png">
        <link rel="stylesheet" href="../css/variables.css">
        <link rel="stylesheet" href="../css/general.css">
        <link rel="stylesheet" href="../css/chrome.css">
        <link rel="stylesheet" href="../css/print.css" media="print">
        <!-- Fonts -->
        <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../fonts/fonts.css">
        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../highlight.css">
        <link rel="stylesheet" href="../tomorrow-night.css">
        <link rel="stylesheet" href="../ayu-highlight.css">

        <!-- Custom theme stylesheets -->
    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded affix "><a href="../part0_introduction.html">Learning gem-5</a></li><li class="chapter-item expanded "><a href="../part0_introduction.html"><strong aria-hidden="true">1.</strong> part0_introduction</a></li><li class="chapter-item expanded "><a href="../part1/part1_1_building.html"><strong aria-hidden="true">2.</strong> part1</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part1/part1_1_building.html"><strong aria-hidden="true">2.1.</strong> part1_1_building</a></li><li class="chapter-item expanded "><a href="../part1/part1_2_simple_config.html"><strong aria-hidden="true">2.2.</strong> part1_2_simple_config</a></li><li class="chapter-item expanded "><a href="../part1/part1_3_cache_config.html"><strong aria-hidden="true">2.3.</strong> part1_3_cache_config</a></li><li class="chapter-item expanded "><a href="../part1/part1_4_gem5_stats.html"><strong aria-hidden="true">2.4.</strong> part1_4_gem5_stats</a></li><li class="chapter-item expanded "><a href="../part1/part1_5_gem5_example_configs.html"><strong aria-hidden="true">2.5.</strong> part1_5_gem5_example_configs</a></li><li class="chapter-item expanded "><a href="../part1/part1_6_extending_configs.html"><strong aria-hidden="true">2.6.</strong> part1_6_extending_configs</a></li></ol></li><li class="chapter-item expanded "><a href="../part2/part2_0_environment.html"><strong aria-hidden="true">3.</strong> part2</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part2/part2_0_environment.html"><strong aria-hidden="true">3.1.</strong> part2_0_environment</a></li><li class="chapter-item expanded "><a href="../part2/part2_1_helloobject.html"><strong aria-hidden="true">3.2.</strong> part2_1_helloobject</a></li><li class="chapter-item expanded "><a href="../part2/part2_2_debugging.html"><strong aria-hidden="true">3.3.</strong> part2_2_debugging</a></li><li class="chapter-item expanded "><a href="../part2/part2_3_events.html"><strong aria-hidden="true">3.4.</strong> part2_3_events</a></li><li class="chapter-item expanded "><a href="../part2/part2_4_parameters.html"><strong aria-hidden="true">3.5.</strong> part2_4_parameters</a></li><li class="chapter-item expanded "><a href="../part2/part2_5_memoryobject.html"><strong aria-hidden="true">3.6.</strong> part2_5_memoryobject</a></li><li class="chapter-item expanded "><a href="../part2/part2_6_simplecache.html"><strong aria-hidden="true">3.7.</strong> part2_6_simplecache</a></li><li class="chapter-item expanded "><a href="../part2/part2_7_arm_power_modelling.html"><strong aria-hidden="true">3.8.</strong> part2_7_arm_power_modelling</a></li><li class="chapter-item expanded "><a href="../part2/part2_8_arm_dvfs_support.html"><strong aria-hidden="true">3.9.</strong> part2_8_arm_dvfs_support</a></li></ol></li><li class="chapter-item expanded "><a href="../part3/part3_00_MSIntro.html"><strong aria-hidden="true">4.</strong> part3</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part3/part3_00_MSIntro.html"><strong aria-hidden="true">4.1.</strong> part3_00_MSIntro</a></li><li class="chapter-item expanded "><a href="../part3/part3_01_cache-intro.html"><strong aria-hidden="true">4.2.</strong> part3_01_cache-intro</a></li><li class="chapter-item expanded "><a href="../part3/part3_02_cache-declarations.html"><strong aria-hidden="true">4.3.</strong> part3_02_cache-declarations</a></li><li class="chapter-item expanded "><a href="../part3/part3_03_cache-in-ports.html"><strong aria-hidden="true">4.4.</strong> part3_03_cache-in-ports</a></li><li class="chapter-item expanded "><a href="../part3/part3_04_cache_actions.html"><strong aria-hidden="true">4.5.</strong> part3_04_cache_actions</a></li><li class="chapter-item expanded "><a href="../part3/part3_05_cache_transitions.html"><strong aria-hidden="true">4.6.</strong> part3_05_cache_transitions</a></li><li class="chapter-item expanded "><a href="../part3/part3_06_directory.html" class="active"><strong aria-hidden="true">4.7.</strong> part3_06_directory</a></li><li class="chapter-item expanded "><a href="../part3/part3_07_MSIbuilding.html"><strong aria-hidden="true">4.8.</strong> part3_07_MSIbuilding</a></li><li class="chapter-item expanded "><a href="../part3/part3_08_configuration.html"><strong aria-hidden="true">4.9.</strong> part3_08_configuration</a></li><li class="chapter-item expanded "><a href="../part3/part3_09_running.html"><strong aria-hidden="true">4.10.</strong> part3_09_running</a></li><li class="chapter-item expanded "><a href="../part3/part3_10_MSIdebugging.html"><strong aria-hidden="true">4.11.</strong> part3_10_MSIdebugging</a></li><li class="chapter-item expanded "><a href="../part3/part3_11_simple-MI_example.html"><strong aria-hidden="true">4.12.</strong> part3_11_simple-MI_example</a></li></ol></li><li class="chapter-item expanded "><a href="../part4_gem5_101.html"><strong aria-hidden="true">5.</strong> part4_gem5_101</a></li><li class="chapter-item expanded "><a href="../http://doxygen.gem5.org/develop/index.html"><strong aria-hidden="true">6.</strong> part4_gem5_102</a></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">learning-gem5</h1>

                    <div class="right-buttons">
                        <a href="../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>
                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <hr />
<h2>layout: documentation
title: MSI Directory implementation
doc: Learning gem5
parent: part3
permalink: /documentation/learning_gem5/part3/directory/
author: Jason Lowe-Power</h2>
<h1 id="msi-directory-implementation"><a class="header" href="#msi-directory-implementation">MSI Directory implementation</a></h1>
<p>Implementing a directory controller is very similar to the L1 cache
controller, except using a different state machine table. The state
machine fore the directory can be found in Table 8.2 in Sorin et al.
Since things are mostly similar to the L1 cache, this section mostly
just discusses a few more SLICC details and a few differences between
directory controllers and cache controllers. Let's dive straight in and
start modifying a new file <code>MSI-dir.sm</code>.</p>
<pre><code class="language-cpp">machine(MachineType:Directory, &quot;Directory protocol&quot;)
:
  DirectoryMemory * directory;
  Cycles toMemLatency := 1;

MessageBuffer *forwardToCache, network=&quot;To&quot;, virtual_network=&quot;1&quot;,
      vnet_type=&quot;forward&quot;;
MessageBuffer *responseToCache, network=&quot;To&quot;, virtual_network=&quot;2&quot;,
      vnet_type=&quot;response&quot;;

MessageBuffer *requestFromCache, network=&quot;From&quot;, virtual_network=&quot;0&quot;,
      vnet_type=&quot;request&quot;;

MessageBuffer *responseFromCache, network=&quot;From&quot;, virtual_network=&quot;2&quot;,
      vnet_type=&quot;response&quot;;

MessageBuffer *responseFromMemory;

{
. . .
}
</code></pre>
<p>First, there are two parameter to this directory controller,
<code>DirectoryMemory</code> and a <code>toMemLatency</code>. The <code>DirectoryMemory</code> is a
little weird. It is allocated at initialization time such that it can
cover <em>all</em> of physical memory, like a complete directory <em>not a
directory cache</em>. I.e., there are pointers in the <code>DirectoryMemory</code>
object for every 64-byte block in physical memory. However, the actual
entries (as defined below) are lazily created via <code>getDirEntry()</code>. We'll
see more details about <code>DirectoryMemory</code> below.</p>
<p>Next, is the <code>toMemLatency</code> parameter. This will be used in the
<code>enqueue</code> function when enqueuing requests to model the directory
latency. We didn't use a parameter for this in the L1 cache, but it is
simple to make the controller latency parameterized. This parameter
defaults to 1 cycle. It is not required to set a default here. The
default is propagated to the generated SimObject description file as the
default to the SimObject parameter.</p>
<p>Next, we have the message buffers for the directory. Importantly, <em>these
need to have the same virtual network numbers</em> as the message buffers in
the L1 cache. These virtual network numbers are how the Ruby network
directs messages between controllers.</p>
<p>There is also one more special message buffer: <code>responseFromMemory</code>.
This is similar to the <code>mandatoryQueue</code>, except instead of being like a
slave port for CPUs it is like a master port. The <code>responseFromMemory</code>
buffer will deliver response sent across the the memory port, as we will
see below in the action section.</p>
<p>After the parameters and message buffers, we need to declare all of the
states, events, and other local structures.</p>
<pre><code class="language-cpp">state_declaration(State, desc=&quot;Directory states&quot;,
                  default=&quot;Directory_State_I&quot;) {
    // Stable states.
    // NOTE: These are &quot;cache-centric&quot; states like in Sorin et al.
    // However, The access permissions are memory-centric.
    I, AccessPermission:Read_Write,  desc=&quot;Invalid in the caches.&quot;;
    S, AccessPermission:Read_Only,   desc=&quot;At least one cache has the blk&quot;;
    M, AccessPermission:Invalid,     desc=&quot;A cache has the block in M&quot;;

    // Transient states
    S_D, AccessPermission:Busy,      desc=&quot;Moving to S, but need data&quot;;

    // Waiting for data from memory
    S_m, AccessPermission:Read_Write, desc=&quot;In S waiting for mem&quot;;
    M_m, AccessPermission:Read_Write, desc=&quot;Moving to M waiting for mem&quot;;

    // Waiting for write-ack from memory
    MI_m, AccessPermission:Busy,       desc=&quot;Moving to I waiting for ack&quot;;
    SS_m, AccessPermission:Busy,       desc=&quot;Moving to I waiting for ack&quot;;
}

enumeration(Event, desc=&quot;Directory events&quot;) {
    // Data requests from the cache
    GetS,         desc=&quot;Request for read-only data from cache&quot;;
    GetM,         desc=&quot;Request for read-write data from cache&quot;;

    // Writeback requests from the cache
    PutSNotLast,  desc=&quot;PutS and the block has other sharers&quot;;
    PutSLast,     desc=&quot;PutS and the block has no other sharers&quot;;
    PutMOwner,    desc=&quot;Dirty data writeback from the owner&quot;;
    PutMNonOwner, desc=&quot;Dirty data writeback from non-owner&quot;;

    // Cache responses
    Data,         desc=&quot;Response to fwd request with data&quot;;

    // From Memory
    MemData,      desc=&quot;Data from memory&quot;;
    MemAck,       desc=&quot;Ack from memory that write is complete&quot;;
}

structure(Entry, desc=&quot;...&quot;, interface=&quot;AbstractEntry&quot;) {
    State DirState,         desc=&quot;Directory state&quot;;
    NetDest Sharers,        desc=&quot;Sharers for this block&quot;;
    NetDest Owner,          desc=&quot;Owner of this block&quot;;
}
</code></pre>
<p>In the <code>state_declaration</code> we define a default. For many things in SLICC
you can specify a default. However, this default must use the C++ name
(mangled SLICC name). For the state below you have to use the controller
name and the name we use for states. In this case, since the name of the
machine is &quot;Directory&quot; the name for &quot;I&quot; is &quot;Directory&quot;+&quot;State&quot; (for the
name of the structure)+&quot;I&quot;.</p>
<p>Note that the permissions in the directory are &quot;memory-centric&quot;.
Whereas, all of the states are cache centric as in Sorin et al.</p>
<p>In the <code>Entry</code> definition for the directory, we use a NetDest for both
the sharers and the owner. This makes sense for the sharers, since we
want a full bitvector for all L1 caches that may be sharing the block.
The reason we also use a <code>NetDest</code> for the owner is to simply copy the
structure into the message we send as a response as shown below.</p>
<p>In this implementation, we use a few more transient states than in Table
8.2 in Sorin et al. to deal with the fact that the memory latency in
unknown. In Sorin et al., the authors assume that the directory state
and memory data is stored together in main-memory to simplify the
protocol. Similarly, we also include new actions: the responses from
memory.</p>
<p>Next, we have the functions that need to overridden and declared. The
function <code>getDirectoryEntry</code> either returns the valid directory entry,
or, if it hasn't been allocated yet, this allocates the entry.
Implementing it this way may save some host memory since this is lazily
populated.</p>
<pre><code class="language-cpp">Tick clockEdge();

Entry getDirectoryEntry(Addr addr), return_by_pointer = &quot;yes&quot; {
    Entry dir_entry := static_cast(Entry, &quot;pointer&quot;, directory[addr]);
    if (is_invalid(dir_entry)) {
        // This first time we see this address allocate an entry for it.
        dir_entry := static_cast(Entry, &quot;pointer&quot;,
                                 directory.allocate(addr, new Entry));
    }
    return dir_entry;
}

State getState(Addr addr) {
    if (directory.isPresent(addr)) {
        return getDirectoryEntry(addr).DirState;
    } else {
        return State:I;
    }
}

void setState(Addr addr, State state) {
    if (directory.isPresent(addr)) {
        if (state == State:M) {
            DPRINTF(RubySlicc, &quot;Owner %s\n&quot;, getDirectoryEntry(addr).Owner);
            assert(getDirectoryEntry(addr).Owner.count() == 1);
            assert(getDirectoryEntry(addr).Sharers.count() == 0);
        }
        getDirectoryEntry(addr).DirState := state;
        if (state == State:I)  {
            assert(getDirectoryEntry(addr).Owner.count() == 0);
            assert(getDirectoryEntry(addr).Sharers.count() == 0);
        }
    }
}

AccessPermission getAccessPermission(Addr addr) {
    if (directory.isPresent(addr)) {
        Entry e := getDirectoryEntry(addr);
        return Directory_State_to_permission(e.DirState);
    } else  {
        return AccessPermission:NotPresent;
    }
}
void setAccessPermission(Addr addr, State state) {
    if (directory.isPresent(addr)) {
        Entry e := getDirectoryEntry(addr);
        e.changePermission(Directory_State_to_permission(state));
    }
}

void functionalRead(Addr addr, Packet *pkt) {
    functionalMemoryRead(pkt);
}

int functionalWrite(Addr addr, Packet *pkt) {
    if (functionalMemoryWrite(pkt)) {
        return 1;
    } else {
        return 0;
    }
</code></pre>
<p>Next, we need to implement the ports for the cache. First we specify the
<code>out_port</code> and then the <code>in_port</code> code blocks. The only difference
between the <code>in_port</code> in the directory and in the L1 cache is that the
directory does not have a TBE or cache entry. Thus, we do not pass
either into the <code>trigger</code> function.</p>
<pre><code class="language-cpp">out_port(forward_out, RequestMsg, forwardToCache);
out_port(response_out, ResponseMsg, responseToCache);

in_port(memQueue_in, MemoryMsg, responseFromMemory) {
    if (memQueue_in.isReady(clockEdge())) {
        peek(memQueue_in, MemoryMsg) {
            if (in_msg.Type == MemoryRequestType:MEMORY_READ) {
                trigger(Event:MemData, in_msg.addr);
            } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) {
                trigger(Event:MemAck, in_msg.addr);
            } else {
                error(&quot;Invalid message&quot;);
            }
        }
    }
}

in_port(response_in, ResponseMsg, responseFromCache) {
    if (response_in.isReady(clockEdge())) {
        peek(response_in, ResponseMsg) {
            if (in_msg.Type == CoherenceResponseType:Data) {
                trigger(Event:Data, in_msg.addr);
            } else {
                error(&quot;Unexpected message type.&quot;);
            }
        }
    }
}

in_port(request_in, RequestMsg, requestFromCache) {
    if (request_in.isReady(clockEdge())) {
        peek(request_in, RequestMsg) {
            Entry e := getDirectoryEntry(in_msg.addr);
            if (in_msg.Type == CoherenceRequestType:GetS) {

                trigger(Event:GetS, in_msg.addr);
            } else if (in_msg.Type == CoherenceRequestType:GetM) {
                trigger(Event:GetM, in_msg.addr);
            } else if (in_msg.Type == CoherenceRequestType:PutS) {
                assert(is_valid(e));
                // If there is only a single sharer (i.e., the requestor)
                if (e.Sharers.count() == 1) {
                    assert(e.Sharers.isElement(in_msg.Requestor));
                    trigger(Event:PutSLast, in_msg.addr);
                } else {
                    trigger(Event:PutSNotLast, in_msg.addr);
                }
            } else if (in_msg.Type == CoherenceRequestType:PutM) {
                assert(is_valid(e));
                if (e.Owner.isElement(in_msg.Requestor)) {
                    trigger(Event:PutMOwner, in_msg.addr);
                } else {
                    trigger(Event:PutMNonOwner, in_msg.addr);
                }
            } else {
                error(&quot;Unexpected message type.&quot;);
            }
        }
    }
}
</code></pre>
<p>The next part of the state machine file is the actions. First, we define
actions for queuing memory reads and writes. For this, we will use a
special function define in the <code>AbstractController</code>: <code>queueMemoryRead</code>.
This function takes an address and converts it to a gem5 request and
packet and sends it to across the port that is connected to this
controller. We will see how to connect this port in the
configuration section &lt;MSI-config-section&gt;. Note that we need two
different actions to send data to memory for both requests and responses
since there are two different message buffers (virtual networks) that
data might arrive on.</p>
<pre><code class="language-cpp">action(sendMemRead, &quot;r&quot;, desc=&quot;Send a memory read request&quot;) {
    peek(request_in, RequestMsg) {
        queueMemoryRead(in_msg.Requestor, address, toMemLatency);
    }
}

action(sendDataToMem, &quot;w&quot;, desc=&quot;Write data to memory&quot;) {
    peek(request_in, RequestMsg) {
        DPRINTF(RubySlicc, &quot;Writing memory for %#x\n&quot;, address);
        DPRINTF(RubySlicc, &quot;Writing %s\n&quot;, in_msg.DataBlk);
        queueMemoryWrite(in_msg.Requestor, address, toMemLatency,
                         in_msg.DataBlk);
    }
}

action(sendRespDataToMem, &quot;rw&quot;, desc=&quot;Write data to memory from resp&quot;) {
    peek(response_in, ResponseMsg) {
        DPRINTF(RubySlicc, &quot;Writing memory for %#x\n&quot;, address);
        DPRINTF(RubySlicc, &quot;Writing %s\n&quot;, in_msg.DataBlk);
        queueMemoryWrite(in_msg.Sender, address, toMemLatency,
                         in_msg.DataBlk);
    }
}
</code></pre>
<p>In this code, we also see the last way to add debug information to SLICC
protocols: <code>DPRINTF</code>. This is exactly the same as a <code>DPRINTF</code> in gem5,
except in SLICC only the <code>RubySlicc</code> debug flag is available.</p>
<p>Next, we specify actions to update the sharers and owner of a particular
block.</p>
<pre><code class="language-cpp">action(addReqToSharers, &quot;aS&quot;, desc=&quot;Add requestor to sharer list&quot;) {
    peek(request_in, RequestMsg) {
        getDirectoryEntry(address).Sharers.add(in_msg.Requestor);
    }
}

action(setOwner, &quot;sO&quot;, desc=&quot;Set the owner&quot;) {
    peek(request_in, RequestMsg) {
        getDirectoryEntry(address).Owner.add(in_msg.Requestor);
    }
}

action(addOwnerToSharers, &quot;oS&quot;, desc=&quot;Add the owner to sharers&quot;) {
    Entry e := getDirectoryEntry(address);
    assert(e.Owner.count() == 1);
    e.Sharers.addNetDest(e.Owner);
}

action(removeReqFromSharers, &quot;rS&quot;, desc=&quot;Remove requestor from sharers&quot;) {
    peek(request_in, RequestMsg) {
        getDirectoryEntry(address).Sharers.remove(in_msg.Requestor);
    }
}

action(clearSharers, &quot;cS&quot;, desc=&quot;Clear the sharer list&quot;) {
    getDirectoryEntry(address).Sharers.clear();
}

action(clearOwner, &quot;cO&quot;, desc=&quot;Clear the owner&quot;) {
    getDirectoryEntry(address).Owner.clear();
}
</code></pre>
<p>The next set of actions send invalidates and forward requests to caches
that the directory cannot deal with alone.</p>
<pre><code class="language-cpp">action(sendInvToSharers, &quot;i&quot;, desc=&quot;Send invalidate to all sharers&quot;) {
    peek(request_in, RequestMsg) {
        enqueue(forward_out, RequestMsg, 1) {
            out_msg.addr := address;
            out_msg.Type := CoherenceRequestType:Inv;
            out_msg.Requestor := in_msg.Requestor;
            out_msg.Destination := getDirectoryEntry(address).Sharers;
            out_msg.MessageSize := MessageSizeType:Control;
        }
    }
}

action(sendFwdGetS, &quot;fS&quot;, desc=&quot;Send forward getS to owner&quot;) {
    assert(getDirectoryEntry(address).Owner.count() == 1);
    peek(request_in, RequestMsg) {
        enqueue(forward_out, RequestMsg, 1) {
            out_msg.addr := address;
            out_msg.Type := CoherenceRequestType:GetS;
            out_msg.Requestor := in_msg.Requestor;
            out_msg.Destination := getDirectoryEntry(address).Owner;
            out_msg.MessageSize := MessageSizeType:Control;
        }
    }
}

action(sendFwdGetM, &quot;fM&quot;, desc=&quot;Send forward getM to owner&quot;) {
    assert(getDirectoryEntry(address).Owner.count() == 1);
    peek(request_in, RequestMsg) {
        enqueue(forward_out, RequestMsg, 1) {
            out_msg.addr := address;
            out_msg.Type := CoherenceRequestType:GetM;
            out_msg.Requestor := in_msg.Requestor;
            out_msg.Destination := getDirectoryEntry(address).Owner;
            out_msg.MessageSize := MessageSizeType:Control;
        }
    }
}
</code></pre>
<p>Now we have responses from the directory. Here we are peeking into the
special buffer <code>responseFromMemory</code>. You can find the definition of
<code>MemoryMsg</code> in <code>src/mem/protocol/RubySlicc_MemControl.sm</code>.</p>
<pre><code class="language-cpp">action(sendDataToReq, &quot;d&quot;, desc=&quot;Send data from memory to requestor. May need to send sharer number, too&quot;) {
    peek(memQueue_in, MemoryMsg) {
        enqueue(response_out, ResponseMsg, 1) {
            out_msg.addr := address;
            out_msg.Type := CoherenceResponseType:Data;
            out_msg.Sender := machineID;
            out_msg.Destination.add(in_msg.OriginalRequestorMachId);
            out_msg.DataBlk := in_msg.DataBlk;
            out_msg.MessageSize := MessageSizeType:Data;
            Entry e := getDirectoryEntry(address);
            // Only need to include acks if we are the owner.
            if (e.Owner.isElement(in_msg.OriginalRequestorMachId)) {
                out_msg.Acks := e.Sharers.count();
            } else {
                out_msg.Acks := 0;
            }
            assert(out_msg.Acks &gt;= 0);
        }
    }
}

action(sendPutAck, &quot;a&quot;, desc=&quot;Send the put ack&quot;) {
    peek(request_in, RequestMsg) {
        enqueue(forward_out, RequestMsg, 1) {
            out_msg.addr := address;
            out_msg.Type := CoherenceRequestType:PutAck;
            out_msg.Requestor := machineID;
            out_msg.Destination.add(in_msg.Requestor);
            out_msg.MessageSize := MessageSizeType:Control;
        }
    }
}
</code></pre>
<p>Then, we have the queue management and stall actions.</p>
<pre><code class="language-cpp">action(popResponseQueue, &quot;pR&quot;, desc=&quot;Pop the response queue&quot;) {
    response_in.dequeue(clockEdge());
}

action(popRequestQueue, &quot;pQ&quot;, desc=&quot;Pop the request queue&quot;) {
    request_in.dequeue(clockEdge());
}

action(popMemQueue, &quot;pM&quot;, desc=&quot;Pop the memory queue&quot;) {
    memQueue_in.dequeue(clockEdge());
}

action(stall, &quot;z&quot;, desc=&quot;Stall the incoming request&quot;) {
    // Do nothing.
}
</code></pre>
<p>Finally, we have the transition section of the state machine file. These
mostly come from Table 8.2 in Sorin et al., but there are some extra
transitions to deal with the unknown memory latency.</p>
<pre><code class="language-cpp">transition({I, S}, GetS, S_m) {
    sendMemRead;
    addReqToSharers;
    popRequestQueue;
}

transition(I, {PutSNotLast, PutSLast, PutMNonOwner}) {
    sendPutAck;
    popRequestQueue;
}

transition(S_m, MemData, S) {
    sendDataToReq;
    popMemQueue;
}

transition(I, GetM, M_m) {
    sendMemRead;
    setOwner;
    popRequestQueue;
}

transition(M_m, MemData, M) {
    sendDataToReq;
    clearSharers; // NOTE: This isn't *required* in some cases.
    popMemQueue;
}

transition(S, GetM, M_m) {
    sendMemRead;
    removeReqFromSharers;
    sendInvToSharers;
    setOwner;
    popRequestQueue;
}

transition({S, S_D, SS_m, S_m}, {PutSNotLast, PutMNonOwner}) {
    removeReqFromSharers;
    sendPutAck;
    popRequestQueue;
}

transition(S, PutSLast, I) {
    removeReqFromSharers;
    sendPutAck;
    popRequestQueue;
}

transition(M, GetS, S_D) {
    sendFwdGetS;
    addReqToSharers;
    addOwnerToSharers;
    clearOwner;
    popRequestQueue;
}

transition(M, GetM) {
    sendFwdGetM;
    clearOwner;
    setOwner;
    popRequestQueue;
}

transition({M, M_m, MI_m}, {PutSNotLast, PutSLast, PutMNonOwner}) {
    sendPutAck;
    popRequestQueue;
}

transition(M, PutMOwner, MI_m) {
    sendDataToMem;
    clearOwner;
    sendPutAck;
    popRequestQueue;
}

transition(MI_m, MemAck, I) {
    popMemQueue;
}

transition(S_D, {GetS, GetM}) {
    stall;
}

transition(S_D, PutSLast) {
    removeReqFromSharers;
    sendPutAck;
    popRequestQueue;
}

transition(S_D, Data, SS_m) {
    sendRespDataToMem;
    popResponseQueue;
}

transition(SS_m, MemAck, S) {
    popMemQueue;
}

// If we get another request for a block that's waiting on memory,
// stall that request.
transition({MI_m, SS_m, S_m, M_m}, {GetS, GetM}) {
    stall;
}
</code></pre>
<p>You can download the complete <code>MSI-dir.sm</code> file
<a href="/_pages/static/scripts/part3/MSI_protocol/MSI-dir.sm">here</a>.</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../part3/part3_05_cache_transitions.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>
                            <a rel="next" href="../part3/part3_07_MSIbuilding.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>
                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../part3/part3_05_cache_transitions.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>
                    <a rel="next" href="../part3/part3_07_MSIbuilding.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>

        <script type="text/javascript">
            window.playground_copyable = true;
        </script>
        <script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
        <script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->
    </body>
</html>
